# -*- coding:utf-8 -*-

# @Time    : 2023/11/20 03:48
# @Author  : zengwenjia
# @Email   : zengwenjia@lingxi.ai
# @File    : dynamic_matching.py
# @Software: LLM_internal

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

import asyncio
import json
import uuid
import re

from bot.insurance_planner_gpt.agent.plan_explain import PlanDescription
from bot.insurance_planner_gpt.utils.causal_data_process_util import CausalDataProcessUtil
from bot.insurance_planner_gpt.utils.seat_planner_msg_channel import SeatPlannerMsgChannel
from common import constants
from common.message import ConversationMessage
from db.redis.redis_service import RedisMemory
from knowledge_base.knowledge_embedding import BGEQuery
from common.log import logger
from bot.insurance_planner_gpt.agent.script.premium_calculation import PremiumCalculation
from bot.insurance_planner_gpt.user_info import UserInfo
from bot.insurance_planner_gpt.agent.communication_strategy import CommunicationStrategy
from bot.insurance_planner_gpt.agent.causal_solution import CausalSolution
from bot.insurance_planner_gpt.agent.insurance_underwriting import InsuranceUnderwriting


class MatchSolution:
    knowledge_base_query = BGEQuery()
    knowledge_base_query.load_all_knowledge_base()
    causalDataProcessUtil = CausalDataProcessUtil()
    memory_cache = RedisMemory()
    process_seat_channel = SeatPlannerMsgChannel()
    insurance_underwriting = InsuranceUnderwriting()

    def __int__(self):
        pass

    async def causal_match(self, session_id, user_question, user_info, candidate_products, knowledge_infos, conversation_history):

        result_json = {}
        # 方案（因果图）构建
        # step1：用户问题获取方案知识
        solutions_infos = []
        questions = user_question.split("\n")
        # user_question 只取最后3个用户偏好或者当下关注的问题
        cur_quesitons = [q for q in questions if "（偏好）" in q or "（当下关注）" in q]
        # 如果没有就直接取最后3个问题
        if not cur_quesitons:
            cur_quesitons = questions[-3:] if len(questions) >= 3 else questions
        cur_quesitons = cur_quesitons[-3:] if len(cur_quesitons) >= 3 else cur_quesitons
        for question in cur_quesitons:
            if question:
                # 去掉用户偏好这些标记，再来检索知识
                question = re.sub(r"（偏好）|（当下关注）", "", question)
                logger.info(f"会话id:{session_id},检索方案的用户问题:{question}")
                raw_solutions = self.knowledge_base_query.search_solutions(question, top_k=8, if_print_score=False,stage3_topk=35)
                if raw_solutions:
                    # step2：根据情境找到合适的方案（留下合适的top产品；此外核查投保要求，如果不能买，给出不能买原因）
                    insurance_person_info = await self.insurance_underwriting.get_insurance_person_info(user_info)
                    match_solution_infos = await self.causalDataProcessUtil.match_solution_info(user_info,
                                                                                          insurance_person_info,
                                                                                          raw_solutions, top_k=5, stage3_topk=12)
                    solutions_infos = solutions_infos + match_solution_infos
        logger.info(f"会话id:{session_id},方案检索结果:{solutions_infos}")
        # step2：构建因果图
        causal_list_str = await self.process_product_causal_solution(conversation_history, knowledge_infos, session_id,
                                                                     solutions_infos, user_info, user_question, candidate_products)
        # step3：沟通策略（干预策略）制定
        communication_strategy_str = ""
        # plan_description = ""
        background_knowledge = []
        cal_result_str = ""
        company_knowledge = ''
        if causal_list_str:
            # 沟通策略（干预策略）制定
            communication_strategy_str = await self.process_communication_strategy(causal_list_str,
                                                                                   conversation_history, session_id,
                                                                                   user_info, user_question)
            # communication_strategy_str ="保险规划_用户30岁，年保费预算3000元的终身重疾险推荐->重疾险（保险类型）->终身重疾险（保险产品类型）->达尔文8号领航版重大疾病保险（保险产品名称）->用户30岁，年保费预算3008元（需计算）"
            need_calculate = False
            need_explain = False
            need_calculate_list = []
            plan_explain_list = []
            strategy_list_use = []
            strategy_list = communication_strategy_str.split("\n")
            for strategy in strategy_list:
                strategy = strategy.replace("(", "（") \
                    .replace(")", "）").replace("-> ", "->").replace(" ->", "->").replace(":", "：")
                # part1：下一步要沟通的策略，全部检索相关的产品简介、链接（是否在售），作为基础背景知识
                strategy_list_use.append(strategy)
                # part2：算保额保费
                if ("（需计算）" in strategy):
                    if ("（需用户提供）" not in strategy):
                        need_calculate = True
                        need_calculate_list.append(strategy)
                    else:
                        # 删除该（需计算）的策略
                        new_strategy = strategy.replace("（需计算）", "")
                        causal_list_str = causal_list_str.replace(strategy, new_strategy)
                # part3：需要讲解的检索相关知识
                if ("（需讲解）" in strategy) or ("（需对比）" in strategy):
                    need_explain = True
                    plan_explain_list.append(strategy)
            # part1：下一步要沟通的策略，全部检索相关的产品简介、链接（是否在售），作为基础背景知识
            base_product_info = await self.causalDataProcessUtil.get_base_product_info_by_strategy(strategy_list_use)
            if base_product_info:
                background_knowledge.append(base_product_info)
                logger.info(f"会话id:{session_id},检索到的所有产品基础知识是：{base_product_info}")
            # part2：（需讲解）的检索相关知识
            if plan_explain_list and need_explain:
                logger.info(f"需要检索条款知识==========,会话id:{session_id}")
                plan_explain_knowledge = await self.causalDataProcessUtil.gen_explain_background_knowledge_by_strategy(
                    plan_explain_list)
                if plan_explain_knowledge:
                    background_knowledge.append(plan_explain_knowledge)
                logger.info(f"会话id:{session_id},检索到的条款知识是：{plan_explain_knowledge}")
            # part3：（需计算）的算保额保费
            if need_calculate:
                logger.info(f"需要调用计算模块==========,会话id:{session_id}")
                need_calculate_insurance_result_list = await self.causalDataProcessUtil.process_data_from_communication_strategy(
                    need_calculate_list)
                logger.info(f"会话id:{session_id},从因果图整理的待计算信息：{need_calculate_insurance_result_list}")
                user_info_en = UserInfo.get_english_dict(user_info)
                premium_calculation = PremiumCalculation(user_info_en)
                insurance_coverage_cal_result_list = await premium_calculation.calculate_insurance_coverage(
                    need_calculate_insurance_result_list)
                logger.info(f"会话id:{session_id},产品的计算结果是：{need_calculate_insurance_result_list}")
                cal_result_str = await premium_calculation.transfer_product2str(insurance_coverage_cal_result_list)
                logger.info(f"会话id:{session_id},输出内容是：{cal_result_str}")
            # part4：根据用户问题和沟通策略，检索具体公司的知识
            if cur_quesitons:
                company_knowledge_part1 = await self.causalDataProcessUtil.get_company_knowledge_by_user_question(cur_quesitons)
                if company_knowledge_part1:
                    company_knowledge += '\n用户问题的回复话术：\n'+company_knowledge_part1
            if strategy_list_use:
                company_knowledge_part2 = await self.causalDataProcessUtil.get_company_knowledge_by_strategy(strategy_list_use)
                if company_knowledge_part2:
                    company_knowledge += '\n沟通策略的回复话术：\n'+company_knowledge_part2
            logger.info(f"会话id:{session_id},检索到的公司知识是：{company_knowledge}")


                # plan_description = PlanDescription(background_knowledge, plan_explain_list, conversation_history)
                # plan_description_task = plan_description.achat_auto_llm(type="self")
                #
                # plan_description = await plan_description_task
                # logger.info(f"会话id:{session_id},产品解释/对比的结果是:{plan_description}")
                #
                # plan_explain_msg = {'title': '方案讲解：', 'content': plan_description}
                # msg = ConversationMessage.gen_msg(constants.MESSAGE_TYPE_AGENT_SOLUTION, session_id, "",
                #                                   json.dumps(plan_explain_msg, ensure_ascii=False), session_id,
                #                                   role=constants.ROLE_ASSISTANT,
                #                                   message_id=str(uuid.uuid4()).replace("-", ""),
                #                                   action="agent_solution")
                # await self.process_seat_channel.publish_msg(self.process_seat_channel.planner_channel, msg)
        result_json["匹配方案"] = causal_list_str
        result_json["沟通策略"] = communication_strategy_str
        result_json["条款知识"] = '\n\n'.join(background_knowledge)
        result_json["保费计算"] = cal_result_str
        result_json["公司知识"] = company_knowledge

        return result_json

    async def process_communication_strategy(self, causal_list_str, conversation_history,
                                             session_id, user_info, user_question):
        strategy_task = CommunicationStrategy(user_question, user_info, causal_list_str, conversation_history)
        communication_strategy_str = await strategy_task.achat_auto_llm(type="self")
        self.memory_cache.set_memory("planner:task:" + session_id, "CommunicationStrategy",
                                     communication_strategy_str)
        logger.info(f"会话id:{session_id},沟通策略制定的结果是：{communication_strategy_str}")
        # 沟通策略发送坐席
        communication_strategy_msg = {'title': '沟通策略：', 'content': communication_strategy_str}
        msg = ConversationMessage.gen_msg(constants.MESSAGE_TYPE_AGENT_SOLUTION, session_id, "",
                                          json.dumps(communication_strategy_msg, ensure_ascii=False), session_id,
                                          role=constants.ROLE_ASSISTANT,
                                          message_id=str(uuid.uuid4()).replace("-", ""), action="agent_solution")
        await self.process_seat_channel.publish_msg(self.process_seat_channel.planner_channel, msg)
        return communication_strategy_str

    async def process_product_causal_solution(self, conversation_history, knowledge_infos, session_id, solutions_infos,
                                              user_info, user_question, condidate_products):
        # 历史因果图查询
        product_causal_solution_cache = ""
        if self.memory_cache.exist_key("planner:task:" + session_id):
            product_causal_solution_cache = self.memory_cache.get_memory("planner:task:" + session_id,
                                                                         "ProductCausalSolution")
            product_causal_solution_cache = str(product_causal_solution_cache).replace("（当下关注）", "")
            # 历史因果的产品核保 TODO 这个要配合被保人识别正确+因果链的产出需加了被保人，暂时不重要先不上线
            # product_causal_solution_cache = await self.insurance_underwriting.check_history_product_availble(product_causal_solution_cache)
        product_solution = CausalSolution(user_question, user_info, condidate_products, knowledge_infos, solutions_infos,
                                          conversation_history,
                                          product_causal_solution_cache)
        causal_list_str = await product_solution.achat_auto_llm(type="self")
        # 更新因果图记忆
        self.memory_cache.set_memory("planner:task:" + session_id, "ProductCausalSolution", causal_list_str)
        logger.info(f"会话id:{session_id},因果推断的结果是：{causal_list_str}")
        causal_msg = {'title': '因果推断：', 'content': causal_list_str}

        msg = ConversationMessage.gen_msg(constants.MESSAGE_TYPE_AGENT_SOLUTION, session_id, "",
                                          json.dumps(causal_msg, ensure_ascii=False), session_id,
                                          role=constants.ROLE_ASSISTANT,
                                          message_id=str(uuid.uuid4()).replace("-", ""), action="agent_solution")
        await self.process_seat_channel.publish_msg(self.process_seat_channel.planner_channel, msg)
        return causal_list_str


if __name__ == "__main__":
    user_question_str = """
铁甲小保2号少儿长期医疗险什么医院能报销
"""
    conversation_history = """
犀心小助:您好！😄我是犀心小助，您的贴心规划小伙伴！想要了解关于保险的小知识，或者有啥保险方面的问题需要解答，都可以告诉我哦！聊天的同时，生活的小乐趣也不要忘记分享哦！🌟那么，现在有什么可以帮到您的呢？🙌
用户:我想给孩子打疫苗  听说铁甲小宝很不错
犀心小助：是的  您想了解哪方面的呢？
用户:我想知道保费多少钱
"""
    user_info = {}

    knowledge_base_query = BGEQuery()
    knowledge_base_query.load_all_knowledge_base()
    user_question_list = user_question_str.split("\n")
    knowledge_infos = []
    for user_question in user_question_list:
        results = knowledge_base_query.search_product_with_score(user_question, top_k=8)
        if results:
            for result in results:
                knowledge_infos.append(result.metadata)
        # print(knowledge_infos)

    product_solution = MatchSolution()
    product_solution_cots = asyncio.run(
        product_solution.causal_match("", user_question_str, user_info, [], knowledge_infos, conversation_history))
    print(product_solution_cots)
